/* dgl.c -- A implemention of DGL procol 
   Copyright (C) 2007 Libo Jiao

This file is part of GS.
The Gas Server is software of Gas Server solution.*/

#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>

#include "gs.h"
#include "dgl.h"
#include "tank.h"

#define POSIX_TERMIOS 1

static int fd_dgl;
/*
 * Set baudrate, parity and number of bits.
 */
static void setparms(int fd, char *baudr, char *par, char *bits, char *stopb,
                int hwf, int swf)
{
  int spd = -1;
  int newbaud;
  int bit = bits[0];

  struct termios tty;

  tcgetattr(fd, &tty);

  /* We generate mark and space parity ourself. */
  if (bit == '7' && (par[0] == 'M' || par[0] == 'S'))
    bit = '8';

  /* Check if 'baudr' is really a number */
  if ((newbaud = (atol(baudr) / 100)) == 0 && baudr[0] != '0')
    newbaud = -1;

  // printf("baudr = %d\n", newbaud);
  switch (newbaud) {
    case 0:
#ifdef B0
      spd = B0;
#else
      spd = 0;
#endif
      break;
    case 3:	spd = B300;	break;
    case 6:	spd = B600;	break;
    case 12:	spd = B1200;	break;
    case 24:	spd = B2400;	break;
    case 48:	spd = B4800;	break;
    case 96:	spd = B9600;	break;
#ifdef B19200
    case 192:	spd = B19200;	break;
#else /* B19200 */
#  ifdef EXTA
    case 192:	spd = EXTA;	break;
#   else /* EXTA */
    case 192:	spd = B9600;	break;
#   endif /* EXTA */
#endif	 /* B19200 */
#ifdef B38400
    case 384:	spd = B38400;	break;
#else /* B38400 */
#  ifdef EXTB
    case 384:	spd = EXTB;	break;
#   else /* EXTB */
    case 384:	spd = B9600;	break;
#   endif /* EXTB */
#endif	 /* B38400 */
#ifdef B57600
    case 576:	spd = B57600;	break;
#endif
#ifdef B115200
    case 1152:	spd = B115200;	break;
#endif
#ifdef B230400
    case 2304:	spd = B230400;	break;
#endif
  }

#ifdef POSIX_TERMIOS

  if (spd != -1) {
    cfsetospeed(&tty, (speed_t)spd);
    cfsetispeed(&tty, (speed_t)spd);
  }

  switch (bit) {
    case '5':
      tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS5;
      break;
    case '6':
      tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS6;
      break;
    case '7':
      tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS7;
      break;
    case '8':
    default:
      tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;
      break;
  }
  /* Set into raw, no echo mode */
  tty.c_iflag =  IGNBRK;
  tty.c_lflag = 0;
  tty.c_oflag = 0;
  tty.c_cflag |= CLOCAL | CREAD;
#ifdef _DCDFLOW
  tty.c_cflag &= ~CRTSCTS;
#endif
  tty.c_cc[VMIN] = 1;
  tty.c_cc[VTIME] = 5;

  if (swf)
    tty.c_iflag |= IXON | IXOFF;
  else
    tty.c_iflag &= ~(IXON|IXOFF|IXANY);

  tty.c_cflag &= ~(PARENB | PARODD);
  if (par[0] == 'E')
    tty.c_cflag |= PARENB;
  else if (par[0] == 'O')
    tty.c_cflag |= (PARENB | PARODD);

  if (stopb[0] == '2')
    tty.c_cflag |= CSTOPB;
  else
    tty.c_cflag &= ~CSTOPB;

  tcsetattr(fd, TCSANOW, &tty);

#endif /* POSIX_TERMIOS */
}

static unsigned char xorsum(unsigned char *buff, int start, int end)
{
	int i;
	unsigned char value = 0X0;
	
	for (i = start; i < end; i++)
		value ^= buff[i];
		
	return value & 0X7F;
}

static void gs_dgl_send_command(int addr)
{
    unsigned char buf[4];

    buf[0] = (unsigned char) addr;
    buf[1] = 0x16;
    buf[2] = 0x00;
    buf[3] = (buf[0] ^ buf[1] ^ buf[2]) & 0x7f;

   // printf ("DGL Send: %x %x %x %x\n", buf[0], buf[1], buf[2], buf[3]);

    write(fd_dgl, buf,sizeof(buf));
}

static int gs_dgl_recv_command(int addr, DGL_DATA *data)
{
    unsigned char buf[24] = {0};
    int n;

    n = read(fd_dgl, buf, 24);

    if (n >= 12) {
        if ( (buf[0] == (unsigned char) addr) && 
                (buf[1] == 0x16) && 
                (buf[2] == 8) && 
                (buf[11] == xorsum(buf, 0, 11)) )
        {
            data->addr = buf[0];
            data->oil = ((((buf[5] << 7) | buf[4]) << 7) | buf[3]) * 0.01;
            data->water = ((((buf[8] << 7) | buf[7]) << 7) | buf[6]) * 0.01;
            data->temp = (((buf[10] << 7) | buf[9]) >> 6) - 50;

            // printf ("dgl oil = %f water = %f temp = %f\n", data->oil, data->water, data->temp);
            return 0;
        }
    }
    
    data->addr = (unsigned char) addr;
    data->oil = 0;
    data->water = 0;
    data->temp = 0;

    return -1;
}

int gs_dgl_init(const char* dev)
{
    fd_dgl = open (dev, O_RDWR | O_NONBLOCK);
   	if (fd_dgl == -1) {
	   perror ("open");
	   return -1;
	}

	setparms(fd_dgl, "4800", "O", "8", "1", 0, 0);

    return 0;
}


int gs_dgl_data(int addr, DGL_DATA *data)
{
    gs_dgl_send_command(addr);

    sleep(1);

    return gs_dgl_recv_command(addr, data);
}

